package com.yuan.util.file;

import com.yuan.util.string.StrUtil;

import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.text.DecimalFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Explain:     [文件工具类]
 * Date:        [2017/7/19 15:42]
 * Coder:       [YouYuan]
 * Version:     [1.0]
 */
public class FileUtil {

    public static final String PATH_SEPARATORS_SLASH = "/";
    public static final String PATH_SEPARATORS_BACKSLASH = "\\";


    /**
     * 遍历给定的根路径,将该路径下以及所有子路径的所有文件和文件夹保存到一个栈中返回.
     * 采用后进先出的遍历顺序保证了文件对象出现在父文件夹之前
     * 返回的栈不包含根目录
     *
     * @param root 根路径
     * @return Stack
     */
    public static Stack<File> traversePathToStack(File root) {
        if (root == null) {
            return null;
        }
        Stack<File> pathStack = new Stack<File>();//存储文件夹
        Stack<File> fileStack = new Stack<File>();//存储遍历的所有数据
        pathStack.push(root);
        while (!pathStack.isEmpty()) {
            File pop = pathStack.pop();
            if (pop == null) {
                continue;
            }
            if (pop.isDirectory()) {
                File[] files = pop.listFiles();
                for (File file : files) {
                    if (file != null) {
                        if (file.isDirectory()) {
                            pathStack.push(file);
                        }
                        fileStack.push(file);
                    }
                }
            }
        }
        return fileStack;
    }

    public static List<File> listFiles(String root) {
        return listFiles(new File(root), null);
    }

    public static List<File> listFiles(String root, String... suffixs) {
        return listFiles(new File(root), suffixs);
    }

    public static List<File> listFiles(File file, final String... suffixs) {
        List<File> files = new ArrayList<File>();
        if (exists(file)) {
            Stack<File> pathStack = new Stack<File>();//存储文件夹
            pathStack.push(file);
            while (!pathStack.isEmpty()) {
                File pop = pathStack.pop();
                if (pop == null) {
                    continue;
                }
                if (pop.isDirectory()) {
                    File[] fileArr = pop.listFiles(new FileFilter() {
                        @Override
                        public boolean accept(File pathname) {
                            if (pathname != null) {
                                if (pathname.isDirectory()) {
                                    return true;
                                }
                                if (pathname.isFile()) {
                                    if (suffixs != null) {
                                        String fileSuffix = getFileSuffix(pathname);
                                        return StrUtil.strInIgnoreCase(fileSuffix, suffixs);
                                    }
                                    return true;//不过滤后缀，默认返回true
                                }
                            }
                            return false;
                        }
                    });
                    if (fileArr != null) {
                        for (File f : fileArr) {
                            if (f != null) {
                                if (f.isDirectory()) {
                                    pathStack.push(f);
                                }
                                if (f.isFile()) {
                                    files.add(f);
                                }
                            }
                        }
                    }
                }
            }
        }
        return files;
    }

    public static boolean equalsType(File file, String suffix) {
        if (exists(file)) {
            String fileSuffix = getFileSuffix(file);
            return StrUtil.equalsIgnoreCase(suffix, fileSuffix);
        }
        return false;
    }

    public static boolean exists(File file) {
        return file != null && file.exists();
    }

    public static boolean exists(String path) {
        return exists(new File(path));
    }

    /**
     * 遍历给定的根路径,将该路径下以及所有子路径的所有文件和文件夹保存到一个队列中返回.
     * 采用先进先出的遍历顺序保证了文件夹出现在子文件之前
     * 返回的队列不包含根目录
     *
     * @param root 根路径
     * @return Queue
     */
    public static Queue<File> traversePathToQueue(File root) {
        if (root == null) {
            return null;
        }
        Queue<File> pathQueue = new LinkedList<File>();//存储文件夹
        Queue<File> fileQueue = new LinkedList<File>();//存储遍历的所有数据
        pathQueue.add(root);
        while (!pathQueue.isEmpty()) {
            File pop = pathQueue.poll();
            if (pop == null) {
                continue;
            }
            if (pop.isDirectory()) {
                File[] files = pop.listFiles();
                for (File file : files) {
                    if (file != null) {
                        if (file.isDirectory()) {
                            pathQueue.add(file);
                        }
                        fileQueue.add(file);
                    }
                }
            }
        }
        return fileQueue;
    }

    /**
     * 遍历给定的根路径,将该路径下以及所有子路径的所有文件和文件夹保存到一个队列中返回.
     * 采用先进先出的遍历顺序保证了文件夹出现在子文件之前
     * 返回的队列不包含根目录
     *
     * @param root 根路径
     * @return Queue
     */
    public static Queue<File> traversePathToQueue(String root) {
        if (root == null) {
            return null;
        }
        return traversePathToQueue(new File(root));
    }

    /**
     * 遍历给定的根路径,将该路径下以及所有子路径的所有文件和文件夹保存到一个栈中返回.
     * 采用后进先出的遍历顺序保证了文件对象出现在父文件夹之前
     * 返回的栈不包含根目录
     *
     * @param root 根路径
     * @return Stack
     */
    public static Stack<File> traversePathToStack(String root) {
        if (root == null) {
            return null;
        }
        return traversePathToStack(new File(root));
    }

    /**
     * 获取文件后缀名
     *
     * @param fileName 文件名
     * @return
     */
    public static String getFileSuffix(String fileName) {
        if (StrUtil.notEmpty(fileName)) {
            int index = fileName.lastIndexOf(".");
            if (index != -1) {
                return fileName.substring(index, fileName.length()).toLowerCase();
            } else {
                return "";
            }
        }
        return null;
    }

    /**
     * 获取文件后缀名,后缀会自动转换为小写，例如"照片.JPG"返回".jpg"
     *
     * @param file 文件对象
     * @return
     */
    public static String getFileSuffix(File file) {
        if (file != null) {
            return getFileSuffix(file.getName());
        }
        return null;
    }

    /**
     * 在路径前添加路径分隔符"/",返回处理成功的新字符串
     * 若path已经为分隔符起始则返回原字符串
     * 若path为null,返回null
     *
     * @param path 待处理的路径字符串
     * @return
     */
    public static String addFrontSeparator(String path) {
        if (path == null) {
            return null;
        }
        if (path.startsWith("/")) {
            return path;
        }
        return "/" + path;
    }

    /**
     * 在路径后添加路径分隔符"/",返回处理成功的新字符串
     * 若path已经为分隔符结尾则返回原字符串
     * 若path为null,返回null
     *
     * @param path 待处理的路径字符串
     * @return
     */
    public static String addLastSeparator(String path) {
        if (path == null) {
            return null;
        }
        if (path.endsWith("/")) {
            return path;
        }
        return path + "/";
    }

    /**
     * 在路径前后添加路径分隔符"/",返回处理成功的新字符串
     * 该方法不会重复添加分隔符
     * 若path为null,返回null
     *
     * @param path 待处理的路径字符串
     * @return
     */
    public static String addSeparator(String path) {
        return addLastSeparator(addFrontSeparator(path));
    }

    /**
     * 转换路径中的分隔符为“/”，此操作会去除所有多余的分隔符
     *
     * @param path 要转换的路径
     * @return
     */
    public static String coverSeparator(String path) {
        return coverSeparator(path, null);
    }

    /**
     * 转换路径中的分隔符为指定符号，此操作会去除所有多余的分隔符
     *
     * @param path      要转换的路径
     * @param separator 分隔符
     * @return
     */
    public static String coverSeparator(String path, String separator) {
        if (path == null) {
            return null;
        }
        if (separator == null) {
            separator = PATH_SEPARATORS_SLASH;
        }
        path = path.replaceAll("/+", separator)
                .replaceAll("\\\\+", separator)
                .replaceAll(StrUtil.escapeRegexChars(separator) + "+", separator);
        return path;
    }

    /**
     * 格式化路径字符串,将会删除多余的路径分隔符
     *
     * @param path
     * @return
     */
    public static String formatPath(String path) {
        if (StrUtil.isEmpty(path)) {
            return "";
        }
        return path.replaceAll("\\\\+", "/")
                .replaceAll("/+", "/");
    }

    /**
     * 将Base64字符串解码成文件保存到指定路径
     *
     * @param base64Str
     * @param savePath
     * @return
     * @throws Exception
     */
    public static File saveBase64ToFile(String base64Str, String savePath) throws Exception {
        byte[] bytes = StrUtil.base64ToBytes(base64Str);
        FileOutputStream out = new FileOutputStream(savePath);
        out.write(bytes);
        out.close();
        return new File(savePath);
    }

    /**
     * 打开文件管理器
     *
     * @param path
     */
    public static void openFileManageer(String path) {
        String osName = System.getProperty("os.name");
        if (osName != null) {
            try {
                if (osName.contains("Mac")) {
                    Runtime.getRuntime().exec("open " + path);
                } else if (osName.contains("Windows")) {
                    Runtime.getRuntime().exec("cmd /c start " + path);
                } else {
                    throw new RuntimeException("不支持的操作系统:" + osName);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 遍历指定包,获取所有文件name(路径)
     *
     * @param packageName
     * @return
     */
    public static List<String> traversePackageGetAllFileName(String packageName) {
        List<String> fileNames = null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        packageName = StrUtil.trim(packageName);
        if (packageName.startsWith("/")) {
            packageName = packageName.substring(1);
        }
        String packagePath = packageName.replace(".", "/");
        URL url = loader.getResource(packagePath);
        if (url != null) {
            String type = url.getProtocol();
            if (type.equals("file")) {
                fileNames = fileGetAllName(url.getPath());//文件资源在文件系统中
            } else if (type.equals("jar")) {
                fileNames = jarGetAllName(url.getPath());//文件资源在jar包中
            }
        } else {
            fileNames = jarGetAllNames(((URLClassLoader) loader).getURLs(), packagePath);
        }
        return fileNames;
    }

    /**
     * 获取Jar包中所有文件名
     *
     * @param urls        jar包URL路径
     * @param packagePath 包路径
     * @return
     */
    public static List<String> jarGetAllNames(URL[] urls, String packagePath) {
        List<String> myClassName = new ArrayList<String>();
        if (urls != null) {
            for (int i = 0; i < urls.length; i++) {
                URL url = urls[i];
                String urlPath = null;
                try {
                    urlPath = URLDecoder.decode(url.getPath(), "UTF-8");
                } catch (Exception e) {
                    urlPath = url.getPath();
                }
                if (!urlPath.endsWith("jar")) {
                    continue;
                }
                String jarPath = urlPath + "!/" + packagePath;
                myClassName.addAll(jarGetAllName(jarPath));
            }
        }
        return myClassName;
    }

    /**
     * 获取jar包所有文件名
     *
     * @param path jar路径（例如d:/YuanUtil.jar/!com.yuan）
     * @return
     */
    public static List<String> jarGetAllName(String path) {
        List<String> names = new ArrayList<String>();
        String[] jarInfo = path.split("!");
        String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
        String packagePath = jarInfo[1].substring(1);
        try {
            JarFile jarFile = new JarFile(jarFilePath);
            Enumeration<JarEntry> entrys = jarFile.entries();
            while (entrys.hasMoreElements()) {
                JarEntry jarEntry = entrys.nextElement();
                String entryName = jarEntry.getName();
                if (entryName.startsWith(packagePath)) {
                    names.add(entryName);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return names;
    }

    /**
     * 获取指定Root路径下所有文件路径
     *
     * @param path
     * @return
     */
    public static List<String> fileGetAllName(String path) {
        List<String> names = new ArrayList<String>();
        File file = new File(path);
        File[] childFiles = file.listFiles();
        for (File childFile : childFiles) {
            if (childFile.isDirectory()) {
                names.addAll(fileGetAllName(childFile.getPath()));
            } else {
                names.add(childFile.getPath());
            }
        }
        return names;
    }

    /**
     * 创建新文件，同时创建父文件夹
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public static boolean createNewFile(String filePath) throws IOException {
        if (filePath == null) {
            return false;
        }
        return createNewFile(new File(filePath));
    }

    public static boolean createNewFile(File file) throws IOException {
        if (file == null) {
            return false;
        }
        boolean mkdirs = file.mkdirs();
        if (mkdirs) {
            file.delete();
            return file.createNewFile();
        }
        return false;
    }

    /**
     * 创建路径
     *
     * @param path
     * @return
     * @throws IOException
     */
    public static boolean createPath(String path) throws IOException {
        return path != null && createPath(new File(path));
    }

    public static boolean createPath(File file) throws IOException {
        if (file == null) {
            return false;
        }
        return file.mkdirs() && file.delete();
    }

    private static final String[] FILE_SIZE_UNITS = {"B", "kB", "MB", "GB", "TB", "EB"};

    /**
     * 将文件大小（byte）转换为带单位易读的格式
     * @param size 文件大小（byte）
     * @return
     */
    public static String readableFileSize(long size) {
        if (size <= 0L) {
            return "0";
        }
        int digitGroups = (int) (Math.log10(size) / Math.log10(1024.0D));
        return new DecimalFormat("#,##0.##").format(size / Math.pow(1024.0D, digitGroups)) + " " + FILE_SIZE_UNITS[digitGroups];
    }

    public static boolean delete(String path) {
        File file = new File(path);
        return file.delete();
    }
}
